package main

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"net/http"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type (
	JWTClaims struct {
		jwt.StandardClaims
		UserID   int    `json:"user_id"`
		Password string `json:"password"`
		Username string `json:"username"`
	}
)

const (
	ErrorServerBusy = "server is busy"
	ErrorReLogin    = "relogin"
	dsn             = "app:Mf3#La#n#sQ8@@tcp(173.20.0.5:3306)/app?charset=utf8mb4&parseTime=True&loc=Local"
)

var (
	Secret     = "593)(031@1" //salt
	ExpireTime = 3600         //token expire time
	db, _      = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	upgrader   = websocket.Upgrader{}
)

func auth(c *gin.Context) {
	token := c.Request.Header.Get("token")
	if token == "" {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"code": -1})
	} else {
		var claims JWTClaims
		t, err := jwt.ParseWithClaims(token, &claims, func(*jwt.Token) (interface{}, error) {
			return []byte(Secret), nil
		})
		if err != nil {
			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"code": -1})
		} else if t.Valid {
			c.Next()
		} else {
			c.Abort()
		}
	}
}

func genToken(claims *JWTClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signedToken, err := token.SignedString([]byte(Secret))
	if err != nil {
		return "", errors.New(ErrorServerBusy)
	}
	return signedToken, nil
}

func MD5(v string) string {
	d := []byte(v)
	m := md5.New()
	m.Write(d)
	return hex.EncodeToString(m.Sum(nil))
}

func login(c *gin.Context) {
	type Info struct {
		Username string `json:"username"`
		Userpass string `json:"userpass"`
	}
	var info Info
	c.BindJSON(&info)
	// fmt.Println(info)
	if (info.Username != "") && (info.Userpass != "") {
		var count int64
		db.Table("oa_admin").Where("username  = ? and userpass = ?", info.Username, MD5(info.Userpass)).Count(&count)
		if count > 0 {
			claims := &JWTClaims{
				Username: info.Username,
				Password: MD5(info.Userpass),
				StandardClaims: jwt.StandardClaims{
					// ExpiresAt: time.Now().Add(time.Hour * 24 * 15).Unix(), ////token有效期为15天
					ExpiresAt: time.Now().Add(time.Hour * 24 * 15).Unix(),
				},
			}
			token, _ := genToken(claims)
			//查询用户role、uid、city列表(所管理城市cid数组) begin
			var admin struct {
				Id   int64 `json:"uid"`
				Role int64 `json:"role"`
			}
			db.Table("oa_admin").Where("username  = ? and userpass = ?", info.Username, MD5(info.Userpass)).Select("id", "role").Find(&admin)
			// db.Debug().Table("oa_admin").Where("username  = ? and userpass = ?", info.Username, MD5(info.Userpass)).Select("id", "role").Find(&admin)
			// db.Table("oa_admin_city").Where("uid = ?", admin.Id).Select("cid").Rows()
			//从oa_admin_city表中查询出所管理城市cid数组
			var city []int64
			db.Table("oa_admin_city").Where("uid = ?", admin.Id).Select("cid").Find(&city)
			//查询用户role、uid、city列表(所管理城市cid数组)  end
			c.JSON(200, gin.H{"code": 1, "msg": "查询成功", "data": gin.H{"token": token, "role": admin.Role, "uid": admin.Id, "citys": city}})
		} else {
			c.JSON(200, gin.H{"code": 0, "msg": "查询失败", "data": nil})
		}
	}
}

func test(c *gin.Context) {
	c.JSON(200, gin.H{"code": 1, "msg": "查询成功", "data": nil})
}

// 获取oa_user表有多少条数据
func getCountOfUser(c *gin.Context) {
	var count int64
	db.Table("oa_user").Count(&count)
	fmt.Println(count)
	c.JSON(200, gin.H{"code": 1, "msg": "查询成功", "count": count})
}

func main() {
	gin.SetMode(gin.ReleaseMode)
	r := gin.Default()
	mwCORS := cors.New(cors.Config{
		//准许跨域请求网站,多个使用,分开,限制使用*
		AllowOrigins: []string{"*"},
		//准许使用的请求方式
		AllowMethods: []string{"PUT", "PATCH", "POST", "GET", "DELETE"},
		//准许使用的请求表头
		AllowHeaders: []string{"Origin", "Authorization", "Content-Type", "token"},
		//显示的请求表头
		ExposeHeaders: []string{"Content-Type"},
		//凭证共享,确定共享
		AllowCredentials: true,
		//容许跨域的原点网站,可以直接return true就万事大吉了
		AllowOriginFunc: func(origin string) bool {
			return true
		},
		//超时时间设定
		MaxAge: 24 * time.Hour,
	})
	r.Use(mwCORS)
	r.GET("/test", test)
	r.POST("/login", login)
	// r.StaticFS("static", http.Dir("/var/www/water/static/")) //静态目录(不需要token)
	r.Use(auth)                      //以下接口都需要token才能访问
	r.MaxMultipartMemory = 100 << 20 //100 GiB
	// r.StaticFS("static", http.Dir("/var/www/water/static/")) //可访问静态文件目录(需要token)
	r.GET("/usercount", getCountOfUser) //获取oa_user表有多少条数据
	r.Run(":3927")
}
